<!doctype html><html class="use-motion" lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="0axhmy0-lA3xPbR8wPgyWlMZjwWSCfagKukdyeCQ7LU"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css"><meta name="keywords" content="DPDK,Network,VPP,"><link rel="alternate" href="/atom.xml" title="赵占旭的博客" type="application/atom+xml"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0"><meta name="description" content="注：本文是转载，但不是100%的转载，可能稍微有些出入，原文地址点击这里"><meta name="keywords" content="DPDK,Network,VPP"><meta property="og:type" content="article"><meta property="og:title" content="[转]Cisco VPP Feature机制"><meta property="og:url" content="http://zhaozhanxu.com/2017/01/29/VPP/2017-01-29-VPP-Feature/index.html"><meta property="og:site_name" content="赵占旭的博客"><meta property="og:description" content="注：本文是转载，但不是100%的转载，可能稍微有些出入，原文地址点击这里"><meta property="og:locale" content="en"><meta property="og:updated_time" content="2017-12-11T14:31:18.945Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="[转]Cisco VPP Feature机制"><meta name="twitter:description" content="注：本文是转载，但不是100%的转载，可能稍微有些出入，原文地址点击这里"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",sidebar:{position:"left",display:"post",offset:342,offset_float:0},fancybox:!0,motion:!0}</script><link rel="canonical" href="http://zhaozhanxu.com/2017/01/29/VPP/2017-01-29-VPP-Feature/"><title> [转]Cisco VPP Feature机制 | 赵占旭的博客</title></head><body itemscope itemtype="http://schema.org/WebPage" lang="en"><script>!function(e,a,t,c,n,g,o){e.GoogleAnalyticsObject="ga",e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,g=a.createElement("script"),o=a.getElementsByTagName("script")[0],g.async=1,g.src="//www.google-analytics.com/analytics.js",o.parentNode.insertBefore(g,o)}(window,document),ga("create","3a235f2280210fdd34cebc8c3647e5b8","auto"),ga("send","pageview")</script><script type="text/javascript">var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?UA-90327054-1";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script type="text/javascript">!function(){var e=document.createElement("script");e.src="//tajs.qq.com/stats?sId=60806179";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><div class="container one-collumn sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader" style="background-image:url(/images/public/home_bg.jpg)"><div class="header-cover"><div class="header-inner"><a class="site-home" href="/">赵占旭的博客</a><div class="site-meta"><div class="custom-logo-site-title"><div href="/" class="brand"><span class="logo-line-before"><i></i></span> <span class="site-title">赵占旭的博客</span><span class="logo-line-after"><i></i></span></div></div><h1 class="site-subtitle" itemprop="description">一花一世界 一叶一菩提</h1></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-categories"><a href="/categories" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br> Categories</a></li><li class="menu-item menu-item-tags"><a href="/tags" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br> Tags</a></li><li class="menu-item menu-item-archives"><a href="/archives" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> Archives</a></li><li class="menu-item menu-item-about"><a href="/about" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i><br> About</a></li><li class="menu-item menu-item-search"><a href="javascript:" ; class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> Search</a></li></ul><div class="site-search"><div class="popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i></span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"> <input autocapitalize="off" autocomplete="off" autocorrect="off" placeholder="search.placeholder" spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div><div class="header-post"><div class="post-header"><div class="tags"> <a href="/tags/DPDK/" rel="tag" title="DPDK">DPDK</a> <a href="/tags/Network/" rel="tag" title="Network">Network</a> <a href="/tags/VPP/" rel="tag" title="VPP">VPP</a></div><h1>[转]Cisco VPP Feature机制</h1><h2 class="subtitle">Section 16. VPP Feature</h2><div class="post-time"> <span class="post-meta-item-text">Posted on</span> <time itemprop="dateCreated" datetime="2017-01-29T00:00:00+08:00" content="2017-01-29" title="2017-01-29 00:00:00">2017-01-29</time></div></div></div></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><link itemprop="mainEntityOfPage" href="http://zhaozhanxu.com/2017/01/29/VPP/2017-01-29-VPP-Feature/"><span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="赵占旭"><meta itemprop="description" content=""><meta itemprop="image" content="/images/public/me.jpg"></span><span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="赵占旭的博客"> <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject"><img style="display:none" itemprop="url image" alt="赵占旭的博客" src=""></span></span><div class="post-inner"><header class="post-header"><h2 class="post-title" itemprop="name headline"> [转]Cisco VPP Feature机制</h2><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">Posted on</span> <time title="Post created" itemprop="dateCreated datePublished" datetime="2017-01-29T00:00:00+08:00">2017-01-29</time></span></div></header><div class="post-info"><div class="post-categories"> <a class="color" href="/categories/VPP/" rel="tag">VPP</a></div><div class="post-tags"> <a class="color" href="/tags/DPDK/" rel="tag">DPDK</a> <a class="color" href="/tags/Network/" rel="tag">Network</a> <a class="color" href="/tags/VPP/" rel="tag">VPP</a></div><div class="clearfix"></div></div><div class="post-body" itemprop="articleBody"><p><strong>注：本文是转载，但不是100%的转载，可能稍微有些出入，原文地址<a href="http://blog.csdn.net/jqh9804/article/details/54772764" target="_blank" rel="noopener">点击这里</a></strong></p><a id="more"></a><h1 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h1><hr><p>早期的VPP本身的node框架比较固定，各个node之间逻辑连接已经固化。<br>为此新版本增加了feature机制，每个feature是一个node，用户可以启用/停止某个或某些feature。<br>用户也可以自己写插件，把自定义node（自己的业务逻辑）加入到指定位置。</p><h2 id="vnet-feature-arc-registration-t"><a href="#vnet-feature-arc-registration-t" class="headerlink" title="vnet_feature_arc_registration_t"></a><code>vnet_feature_arc_registration_t</code></h2><p>vpp将feature分成不同的组，每组feature称为一个arc。arc中的feature按照代码指定的顺序串接起来。arc结构将记录这组feature的起始node和结束node。系统初始化时完成每个feature的连接。<br><code>VNET_FEATURE_ARC_INIT</code>宏用来注册arc。在arc中指定的起始node中，必须调用<code>vnet_feature_arc_start</code>函数，才能正式进入feature机制业务流程，该函数会将下一跳强行指定为arc中的下一个feature。</p><h2 id="vnet-feature-registration-t"><a href="#vnet-feature-registration-t" class="headerlink" title="vnet_feature_registration_t"></a><code>vnet_feature_registration_t</code></h2><p>一个feature等价于一个node，用户通过<code>VNET_FEATURE_INIT</code>宏定义自己的feature，指定需要加入哪个arc，以及在arc中的哪个相对位置。每个feature都可以通过外部命令行启用/停止。</p><h1 id="关键函数"><a href="#关键函数" class="headerlink" title="关键函数"></a>关键函数</h1><hr><p><code>VNET_FEATURE_ARC_INIT</code>和<code>VNET_FEATURE_ARC_INIT</code>宏将arc和feature保存到全局变量<code>feature_main</code>中。此时arc和feature还未被”加工”，只是基本的原料。</p><h2 id="Feature初始化"><a href="#Feature初始化" class="headerlink" title="Feature初始化"></a>Feature初始化</h2><p><code>clib_error_t *vnet_feature_init</code>将把arc和feature初始化，并组织成最终的数据结构填充到<code>vnet_feature_main_t feature_main</code>中，代码比较简单。这里只对<code>vnet_feature_main_t</code>描述即可。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    <span class="comment">// feature arc configuration list</span></span><br><span class="line">    <span class="comment">//VNET_FEATURE_ARC_INIT宏中注册的arc保存在这里，顺序不定，也没有意义。</span></span><br><span class="line">    <span class="keyword">vnet_feature_arc_registration_t</span> *next_arc;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * vnet_feature_init()中来初始化该成员，</span></span><br><span class="line"><span class="comment">     * 把arc按名字组织成hash表,为何要用双重指针？？？</span></span><br><span class="line"><span class="comment">     * 博主认为是bug，后面明明是当hash表用么。又不是hash数组</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    uword **arc_index_by_name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * feature path configuration lists</span></span><br><span class="line"><span class="comment">     * VNET_FEATURE_ARC_INIT宏中注册的feature保存在这里，顺序不定，也没有意义。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">vnet_feature_registration_t</span> *next_feature;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * vnet_feature_init()中来初始化该成员，把feature分配到不同arc中，</span></span><br><span class="line"><span class="comment">     * 并且根据before和after变量来决定feature顺序。</span></span><br><span class="line"><span class="comment">     * 这里看作是一个数组，用arc的index做索引</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">vnet_feature_registration_t</span> **next_feature_by_arc;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * vnet_feature_init()中来初始化该成员，把feature按名字组织成hash表数组，</span></span><br><span class="line"><span class="comment">     * arc的index索引到对应hash表，node name来做hash表的key</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    uword **next_feature_by_name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// feature config main objects</span></span><br><span class="line">    <span class="keyword">vnet_feature_config_main_t</span> *feature_config_mains;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Save partial order results for show command</span></span><br><span class="line">    <span class="keyword">char</span> ***feature_nodes;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * bitmap of interfaces which have driver rx features configured</span></span><br><span class="line"><span class="comment">     * bitmap数组，arc的index索引对应bitmap表,决定该网卡是否启用了该arc中的feature</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    uword **sw_if_index_has_features;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// feature reference counts by interface</span></span><br><span class="line">    i16 **feature_count_by_sw_if_index;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Feature arc index for device-input</span></span><br><span class="line"><span class="comment">     * 从收包开始就使用feature机制了，这个变量给收包驱动用。</span></span><br><span class="line"><span class="comment">     * 追踪了下代码，这个变量属于device_input_node这个node，</span></span><br><span class="line"><span class="comment">     * 但是这个node业务函数为空，而且状态是VLIB_NODE_STATE_DISABLED。</span></span><br><span class="line"><span class="comment">     * 那这是干什么用的呢？层层追踪，原来这个node纯粹是个dump node，</span></span><br><span class="line"><span class="comment">     * 但是它是一个arc起点。这个arc的index保存在了下面变量中。</span></span><br><span class="line"><span class="comment">     * 然后真正的网卡收包node中会借用这个index当成自己的，</span></span><br><span class="line"><span class="comment">     * 调用vnet_feature_start_device_input_x1这类函数，</span></span><br><span class="line"><span class="comment">     * 开始了feature之旅。真TMD恶心，代码还能写的更难懂点么。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    u8 device_input_feature_arc_index;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// convenience</span></span><br><span class="line">    <span class="keyword">vlib_main_t</span> *vlib_main;</span><br><span class="line">    <span class="keyword">vnet_main_t</span> *vnet_main;</span><br><span class="line">&#125; <span class="keyword">vnet_feature_main_t</span>;</span><br></pre></td></tr></table></figure><h2 id="arc初始化"><a href="#arc初始化" class="headerlink" title="arc初始化"></a>arc初始化</h2><p><code>vnet_feature_arc_init</code>把属于某arc的无序的feature排序成有序状态。feature注册时可以指定在某些node前面，和某些node后面。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">clib_error_t</span> *vnet_feature_arc_init (<span class="keyword">vlib_main_t</span> * vm,</span><br><span class="line">            <span class="keyword">vnet_config_main_t</span> * vcm,</span><br><span class="line">            <span class="keyword">char</span> **feature_start_nodes,</span><br><span class="line">            <span class="keyword">int</span> num_feature_start_nodes,</span><br><span class="line">            <span class="keyword">vnet_feature_registration_t</span> * first_reg,</span><br><span class="line">            <span class="keyword">char</span> ***in_feature_nodes)</span><br><span class="line">&#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">// pass 1, collect feature node names, construct a before b pairs</span></span><br><span class="line">    <span class="keyword">while</span> (this_reg)</span><br><span class="line">    &#123;</span><br><span class="line">        node_name = format (<span class="number">0</span>, <span class="string">"%s%c"</span>, this_reg-&gt;node_name, <span class="number">0</span>);</span><br><span class="line">        <span class="comment">//vec_len (node_names) 指的是当前node_names数组中元素个数</span></span><br><span class="line">        hash_set (reg_by_index, vec_len (node_names), (uword) this_reg);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//现在feature用index来代表了</span></span><br><span class="line">        hash_set_mem (index_by_name, node_name, vec_len (node_names));</span><br><span class="line"></span><br><span class="line">        vec_add1 (node_names, node_name);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//runs_before是个数组，可以保存有多个node。</span></span><br><span class="line">        these_constraints = this_reg-&gt;runs_before;</span><br><span class="line">        <span class="keyword">while</span> (these_constraints &amp;&amp; these_constraints[<span class="number">0</span>])</span><br><span class="line">        &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//runs_after是个数组，可以保存有多个node。</span></span><br><span class="line">        these_constraints = this_reg-&gt;runs_after;</span><br><span class="line">        <span class="keyword">while</span> (these_constraints &amp;&amp; these_constraints[<span class="number">0</span>])</span><br><span class="line">        &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        this_reg = this_reg-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//至此，把feature之间顺序关系表达为"A,B"字符串，代表A在B前面。</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//其实就是该arc中所有feature个数</span></span><br><span class="line">    n_features = vec_len (node_names);</span><br><span class="line">    <span class="comment">//可以看作生成一个(n_features X n_features)的二维数组</span></span><br><span class="line">    orig = clib_ptclosure_alloc (n_features);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; vec_len (constraints); i++)</span><br><span class="line">    &#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* see if we got a partial order... */</span></span><br><span class="line">    <span class="keyword">if</span> (vec_len (result) != n_features)</span><br><span class="line">      <span class="keyword">return</span> clib_error_return (<span class="number">0</span>, <span class="string">"%d feature_init_cast no partial order!"</span>);</span><br><span class="line">    <span class="comment">//到这里，feature的顺序关系就计算成功了。保存在result中，每个feature用index代表。</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * We win.</span></span><br><span class="line"><span class="comment">     * Bind the index variables, and output the feature node name vector</span></span><br><span class="line"><span class="comment">     * using the partial order we just computed. Result is in stack</span></span><br><span class="line"><span class="comment">     * order, because the entry with the fewest constraints (e.g. none)</span></span><br><span class="line"><span class="comment">     * is output first, etc.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//把feature索引按顺序转换成对应的vnet_feature_registration_t信息，保存在feature_nodes中</span></span><br><span class="line">    <span class="keyword">for</span> (i = n_features - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--)</span><br><span class="line">    &#123;</span><br><span class="line">        p = hash_get (reg_by_index, result[i]);</span><br><span class="line">        ASSERT (p != <span class="number">0</span>);</span><br><span class="line">        this_reg = (<span class="keyword">vnet_feature_registration_t</span> *) p[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">if</span> (this_reg-&gt;feature_index_ptr)</span><br><span class="line">          *this_reg-&gt;feature_index_ptr = n_features - (i + <span class="number">1</span>);</span><br><span class="line">        this_reg-&gt;feature_index = n_features - (i + <span class="number">1</span>);</span><br><span class="line">        vec_add1 (feature_nodes, this_reg-&gt;node_name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Set up the config infrastructure */</span></span><br><span class="line">    <span class="comment">/*feature顺序现在保存在feature_nodes中，现在要把这个关系保存到vnet_config_main_t中了。</span></span><br><span class="line"><span class="comment">      注意每个arc都有一份自己的vnet_config_main_t*/</span></span><br><span class="line">    vnet_config_init (vm, vcm,</span><br><span class="line">                feature_start_nodes,</span><br><span class="line">                num_feature_start_nodes,</span><br><span class="line">                feature_nodes, vec_len (feature_nodes));</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Save a copy for show command */</span></span><br><span class="line">    *in_feature_nodes = feature_nodes;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Finally, clean up all the shit we allocated */</span></span><br><span class="line">    <span class="comment">/* *INDENT-OFF* */</span></span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，feature已经计算好了顺序，但是还是没有真正把各个feature连接起来。</p><h2 id="连接Feature"><a href="#连接Feature" class="headerlink" title="连接Feature"></a>连接Feature</h2><p>连接各个feature的真正工作，最终交由<code>vnet_config_add_feature</code>和<code>vnet_config_del_feature</code>完成。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">u32 <span class="title">vnet_config_add_feature</span> <span class="params">(<span class="keyword">vlib_main_t</span> * vm,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">vnet_config_main_t</span> * cm,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 config_string_heap_index,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 feature_index,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">void</span> *feature_config, u32 n_feature_config_bytes)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="comment">//现有的feature基础上增加参数中传来的新feature。</span></span><br><span class="line">    <span class="comment">//注意每个feature都是这么一个个添加进来的。</span></span><br><span class="line">    vec_add2 (new_features, f, <span class="number">1</span>);</span><br><span class="line">    f-&gt;feature_index = feature_index;</span><br><span class="line">    f-&gt;node_index = node_index;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 不同feature可能有自己独特的config内容，</span></span><br><span class="line"><span class="comment">     * 因此参数中的feature_config和n_feature_config_bytes就是用来干这事的，</span></span><br><span class="line"><span class="comment">     * 保存在f-&gt;feature_config中，没有私有内容，则计算出来为0。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    n_feature_config_u32s =</span><br><span class="line">        round_pow2 (n_feature_config_bytes,</span><br><span class="line">                    <span class="keyword">sizeof</span> (f-&gt;feature_config[<span class="number">0</span>])) /</span><br><span class="line">        <span class="keyword">sizeof</span> (f-&gt;feature_config[<span class="number">0</span>]);</span><br><span class="line">    vec_add (f-&gt;feature_config, feature_config, n_feature_config_u32s);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//上文所知，feature的index也代表着在arc中的顺序，这里排下序。</span></span><br><span class="line">    <span class="keyword">if</span> (vec_len (new_features) &gt; <span class="number">1</span>)</span><br><span class="line">      vec_sort_with_function (new_features, feature_cmp);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (old)</span><br><span class="line">      remove_reference (cm, old);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//开始干活了，连接每个feature。config内存也会更新</span></span><br><span class="line">    <span class="keyword">new</span> = find_config_with_features (vm, cm, new_features);</span><br><span class="line">    <span class="keyword">new</span>-&gt;reference_count += <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * User gets pointer to config string first element</span></span><br><span class="line"><span class="comment">     * (which defines the pool index</span></span><br><span class="line"><span class="comment">     * this config string comes from).</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    vec_validate (cm-&gt;config_pool_index_by_user_index,</span><br><span class="line">                <span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>);</span><br><span class="line">    cm-&gt;config_pool_index_by_user_index[<span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>]</span><br><span class="line">        = <span class="keyword">new</span> - cm-&gt;config_pool;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">u32 <span class="title">vnet_config_del_feature</span> <span class="params">(<span class="keyword">vlib_main_t</span> * vm,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">vnet_config_main_t</span> * cm,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 config_string_heap_index,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 feature_index,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">void</span> *feature_config, u32 n_feature_config_bytes)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">vnet_config_t</span> *old, *<span class="keyword">new</span>;</span><br><span class="line">    <span class="keyword">vnet_config_feature_t</span> *new_features, *f;</span><br><span class="line">    u32 n_feature_config_u32s;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//每个feature组合都有一个对应的config内存。</span></span><br><span class="line">    u32 *p = vnet_get_config_heap (cm, config_string_heap_index);</span><br><span class="line">    <span class="comment">//config内存第一个32bit，保存了该config的索引号。</span></span><br><span class="line">    <span class="comment">//这代码很恶心，作者为啥不弄个结构出来，还好懂些。</span></span><br><span class="line">    old = pool_elt_at_index (cm-&gt;config_pool, p[<span class="number">-1</span>]);</span><br><span class="line"></span><br><span class="line">    n_feature_config_u32s =</span><br><span class="line">        round_pow2 (n_feature_config_bytes,</span><br><span class="line">                    <span class="keyword">sizeof</span> (f-&gt;feature_config[<span class="number">0</span>])) /</span><br><span class="line">        <span class="keyword">sizeof</span> (f-&gt;feature_config[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Find feature with same index and opaque data. */</span></span><br><span class="line">    <span class="comment">//找到要删除的目标feature</span></span><br><span class="line">    vec_foreach (f, old-&gt;features)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (f-&gt;feature_index == feature_index</span><br><span class="line">                    &amp;&amp; vec_len (f-&gt;feature_config) == n_feature_config_u32s</span><br><span class="line">                    &amp;&amp; (n_feature_config_u32s == <span class="number">0</span></span><br><span class="line">                        || !<span class="built_in">memcmp</span> (f-&gt;feature_config, feature_config,</span><br><span class="line">                            n_feature_config_bytes)))</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Feature not found. */</span></span><br><span class="line">    <span class="keyword">if</span> (f &gt;= vec_end (old-&gt;features))</span><br><span class="line">      <span class="keyword">return</span> config_string_heap_index;    <span class="comment">// return original config index</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//生成一组新feature，不包含目标feature。</span></span><br><span class="line">    new_features = duplicate_feature_vector (old-&gt;features);</span><br><span class="line">    f = new_features + (f - old-&gt;features);</span><br><span class="line">    vnet_config_feature_free (f);</span><br><span class="line">    vec_delete (new_features, <span class="number">1</span>, f - new_features);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * must remove old from config_pool now as it may be expanded and change</span></span><br><span class="line"><span class="comment">     * memory location if the following function find_config_with_features()</span></span><br><span class="line"><span class="comment">     * adds a new config because none of existing config's has matching features</span></span><br><span class="line"><span class="comment">     * and so can be reused</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    remove_reference (cm, old);</span><br><span class="line">    <span class="comment">//新生成的feature重新连接下，config内存也会更新</span></span><br><span class="line">    <span class="keyword">new</span> = find_config_with_features (vm, cm, new_features);</span><br><span class="line">    <span class="keyword">new</span>-&gt;reference_count += <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    vec_validate (cm-&gt;config_pool_index_by_user_index,</span><br><span class="line">                <span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>);</span><br><span class="line">    cm-&gt;config_pool_index_by_user_index[<span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>]</span><br><span class="line">        = <span class="keyword">new</span> - cm-&gt;config_pool;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span>-&gt;config_string_heap_index + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> vnet_config_t *<span class="title">find_config_with_features</span> <span class="params">(<span class="keyword">vlib_main_t</span> * vm,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">vnet_config_main_t</span> * cm,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">vnet_config_feature_t</span> * feature_vector)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    u32 last_node_index = ~<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">vnet_config_feature_t</span> *f;</span><br><span class="line">    u32 *config_string;</span><br><span class="line">    uword *p;</span><br><span class="line">    <span class="keyword">vnet_config_t</span> *c;</span><br><span class="line"></span><br><span class="line">    config_string = cm-&gt;config_string_temp;</span><br><span class="line">    cm-&gt;config_string_temp = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (config_string)</span><br><span class="line">      _vec_len (config_string) = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    vec_foreach (f, feature_vector)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//按顺序连接各个node</span></span><br><span class="line">        f-&gt;next_index = add_next (vm, cm, last_node_index, f-&gt;node_index);</span><br><span class="line">        last_node_index = f-&gt;node_index;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//下一个node在本node中的slot号，保存进config_string</span></span><br><span class="line">        vec_add1 (config_string, f-&gt;next_index);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//slot号后面还可以保存本node特有的config信息，可以为0大小</span></span><br><span class="line">        vec_add (config_string, f-&gt;feature_config, vec_len (f-&gt;feature_config));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//确保最终连接到end node</span></span><br><span class="line">    <span class="keyword">if</span> (last_node_index == ~<span class="number">0</span> || last_node_index != cm-&gt;end_node_index)</span><br><span class="line">    &#123;</span><br><span class="line">        u32 next_index = add_next (vm, cm, last_node_index, cm-&gt;end_node_index);</span><br><span class="line">        vec_add1 (config_string, next_index);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//看看hash表里是不是有同样的config_string，注意这里比较的是config_string内容，不是地址</span></span><br><span class="line">    p = hash_get_mem (cm-&gt;config_string_hash, config_string);</span><br><span class="line">    <span class="keyword">if</span> (p)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//大多数情况下config_string不会重复，除非旧的还被别的模块引用了没释放，</span></span><br><span class="line">        <span class="comment">//那这里就继续引用旧的释放新的</span></span><br><span class="line">        cm-&gt;config_string_temp = config_string;   <span class="comment">/* we'll use it again later. */</span></span><br><span class="line">        free_feature_vector (feature_vector);</span><br><span class="line">        c = pool_elt_at_index (cm-&gt;config_pool, p[<span class="number">0</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        u32 *d;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//分配新的config结构</span></span><br><span class="line">        pool_get (cm-&gt;config_pool, c);</span><br><span class="line">        c-&gt;index = c - cm-&gt;config_pool;</span><br><span class="line">        c-&gt;features = feature_vector;</span><br><span class="line">        c-&gt;config_string_vector = config_string;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Allocate copy of config string in heap.</span></span><br><span class="line"><span class="comment">         * VLIB buffers will maintain pointers to heap as they read out</span></span><br><span class="line"><span class="comment">         * configuration data.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        c-&gt;config_string_heap_index</span><br><span class="line">            = heap_alloc (cm-&gt;config_string_heap, vec_len (config_string) + <span class="number">1</span>,</span><br><span class="line">                        c-&gt;config_string_heap_handle);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* First element in heap points back to pool index. */</span></span><br><span class="line">        d =</span><br><span class="line">            vec_elt_at_index (cm-&gt;config_string_heap,</span><br><span class="line">                        c-&gt;config_string_heap_index);</span><br><span class="line">        <span class="comment">//注意这里，config第一个4字节保存的是config索引号，</span></span><br><span class="line">        <span class="comment">//之后才是slot号-私有config-slot号-私有config ......</span></span><br><span class="line">        d[<span class="number">0</span>] = c-&gt;index;</span><br><span class="line">        clib_memcpy (d + <span class="number">1</span>, config_string, vec_bytes (config_string));</span><br><span class="line">        hash_set_mem (cm-&gt;config_string_hash, config_string, c-&gt;index);</span><br><span class="line"></span><br><span class="line">        c-&gt;reference_count = <span class="number">0</span>;   <span class="comment">/* will be incremented by caller. */</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> c;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在各个feature node已经连接好了，连接信息也保存到了config中，接下来就是在业务node中使用了。</p><h2 id="启动arc中的Feature"><a href="#启动arc中的Feature" class="headerlink" title="启动arc中的Feature"></a>启动arc中的Feature</h2><p>在需要开始使用feature机制的业务node中调用如下函数即可，注意该业务node必须是arc中的起始node。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">static_always_inline <span class="keyword">void</span> <span class="title">vnet_feature_arc_start</span> <span class="params">(u8 arc,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 sw_if_index, u32 * next0, <span class="keyword">vlib_buffer_t</span> * b0)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    vnet_feature_arc_start_with_data (arc, sw_if_index, next0, b0, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此函数会将next0修改为下一跳feature。feature的顺序在上文中已经确定。</p><p>获取下一跳feature的逻辑很简单，在函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">always_inline <span class="keyword">void</span> *<span class="title">vnet_get_config_data</span> <span class="params">(<span class="keyword">vnet_config_main_t</span> * cm,</span></span></span><br><span class="line"><span class="function"><span class="params">            u32 * config_index, u32 * next_index, u32 n_data_bytes)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    u32 i, n, *d;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//vlib_buffer_t-&gt;current_config_index用来记录执行到了哪个feature了，注意最初值为1。</span></span><br><span class="line">    i = *config_index;</span><br><span class="line"></span><br><span class="line">    d = heap_elt_at_index (cm-&gt;config_string_heap, i);</span><br><span class="line"></span><br><span class="line">    n = round_pow2 (n_data_bytes, <span class="keyword">sizeof</span> (d[<span class="number">0</span>])) / <span class="keyword">sizeof</span> (d[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//本feature下一跳的slot号</span></span><br><span class="line">    *next_index = d[n];</span><br><span class="line"></span><br><span class="line">    <span class="comment">//记录下一跳feature的config索引</span></span><br><span class="line">    *config_index = (i + n + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">void</span> *) d;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><div></div><footer class="post-footer"><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/01/10/VPP/2017-01-10-VPP-Router2/" rel="next" title="[转]Cisco VPP 路由分析下"><i class="fa fa-chevron-left"></i> [转]Cisco VPP 路由分析下</a></div><div class="post-nav-prev post-nav-item"> <a href="/2017/02/03/SDN/OVS/2017-02-03-analyzing-bottlenecks/" rel="prev" title="OVS-DPDK 瓶颈分析">OVS-DPDK 瓶颈分析<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-copyright"><p><span>注意:</span>所有文章非特别说明皆为原创。为保证信息与源同步，转载时请务必注明文章出处！谢谢合作 :-)</p><p> <span>原始链接:</span><a class="post-url" href="/2017/01/29/VPP/2017-01-29-VPP-Feature/" title="[转]Cisco VPP Feature机制">http://zhaozhanxu.com/2017/01/29/VPP/2017-01-29-VPP-Feature/</a><span class="copy-path" data-clipboard-text="原文: http://zhaozhanxu.com/2017/01/29/VPP/2017-01-29-VPP-Feature/　　作者: 赵占旭" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span><script src="/js/clipboard.min.js"></script><script>var clipboard=new Clipboard(".copy-path")</script></p><p> <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/cn" title="中国大陆 (CC BY-NC-SA 4.0 CN)" target="_blank">"署名-非商用-相同方式共享 4.0"</a> 转载请保留原文链接及作者。</p></div></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> Table of Contents</li><li class="sidebar-nav-overview" data-target="site-overview"> Overview</li></ul><section class="site-overview sidebar-panel"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a href="/about"><img class="site-author-image" itemprop="image" src="/images/public/me.jpg" alt="赵占旭"><p class="site-author-name" itemprop="name">赵占旭</p><p class="site-description motion-element" itemprop="description"></p></a></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives"><span class="site-state-item-count">61</span> <span class="site-state-item-name">posts</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories"><span class="site-state-item-count">7</span> <span class="site-state-item-name">categories</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags"><span class="site-state-item-count">14</span> <span class="site-state-item-name">tags</span></a></div></nav><div class="feed-link motion-element"><a href="/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="mailto:zhaozhanxu@163.com" target="_blank" title="Mail"><i class="fa fa-fw fa-envelope"></i> Mail</a></span><span class="links-of-author-item"><a href="https://github.com/zhaozhanxu" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="https://www.linkedin.com/in/zhaozhanxu" target="_blank" title="LinkedIn"><i class="fa fa-fw fa-linkedin"></i> LinkedIn</a></span><span class="links-of-author-item"><a href="https://twitter.com/zhaozhanxu" target="_blank" title="Twitter"><i class="fa fa-fw fa-twitter"></i> Twitter</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="Wechat"><i class="fa fa-fw fa-wechat"></i> Wechat</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="QQ"><i class="fa fa-fw fa-qq"></i> QQ</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-inline"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-address-book"></i> 基情链接</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="http://blog.csdn.net/jqh9804" title="呆子辉" target="_blank">呆子辉</a></li></ul></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#基本概念"><span class="nav-number">1.</span><span class="nav-text"><a href="#&#x57FA;&#x672C;&#x6982;&#x5FF5;" class="headerlink" title="&#x57FA;&#x672C;&#x6982;&#x5FF5;"></a> &#x57FA;&#x672C;&#x6982;&#x5FF5;</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#vnet-feature-arc-registration-t"><span class="nav-number">1.1.</span><span class="nav-text"><a href="#vnet-feature-arc-registration-t" class="headerlink" title="vnet_feature_arc_registration_t"></a> <code>vnet_feature_arc_registration_t</code></span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#vnet-feature-registration-t"><span class="nav-number">1.2.</span><span class="nav-text"><a href="#vnet-feature-registration-t" class="headerlink" title="vnet_feature_registration_t"></a> <code>vnet_feature_registration_t</code></span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#关键函数"><span class="nav-number">2.</span><span class="nav-text"><a href="#&#x5173;&#x952E;&#x51FD;&#x6570;" class="headerlink" title="&#x5173;&#x952E;&#x51FD;&#x6570;"></a> &#x5173;&#x952E;&#x51FD;&#x6570;</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Feature初始化"><span class="nav-number">2.1.</span><span class="nav-text"><a href="#Feature&#x521D;&#x59CB;&#x5316;" class="headerlink" title="Feature&#x521D;&#x59CB;&#x5316;"></a> Feature&#x521D;&#x59CB;&#x5316;</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#arc初始化"><span class="nav-number">2.2.</span><span class="nav-text"><a href="#arc&#x521D;&#x59CB;&#x5316;" class="headerlink" title="arc&#x521D;&#x59CB;&#x5316;"></a> arc&#x521D;&#x59CB;&#x5316;</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#连接Feature"><span class="nav-number">2.3.</span><span class="nav-text"><a href="#&#x8FDE;&#x63A5;Feature" class="headerlink" title="&#x8FDE;&#x63A5;Feature"></a> &#x8FDE;&#x63A5;Feature</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#启动arc中的Feature"><span class="nav-number">2.4.</span><span class="nav-text"><a href="#&#x542F;&#x52A8;arc&#x4E2D;&#x7684;Feature" class="headerlink" title="&#x542F;&#x52A8;arc&#x4E2D;&#x7684;Feature"></a> &#x542F;&#x52A8;arc&#x4E2D;&#x7684;Feature</span></a></li></ol></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright"> &copy; 2016 - <span itemprop="copyrightYear">2017</span><span class="with-love"><i class="fa fa-heart"></i></span> <span class="author" itemprop="copyrightHolder">赵占旭</span></div><div class="powered-by"> Powered by <a class="theme-link" href="https://hexo.io">Hexo</a></div><div class="theme-info"> Theme - <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">NexT</a></div><div class="busuanzi-count"><script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="site-uv"><i class="fa fa-user"></i><span class="busuanzi-value" id="busuanzi_value_site_uv"></span></span><span class="site-pv"><i class="fa fa-eye"></i><span class="busuanzi-value" id="busuanzi_value_page_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script><script type="text/javascript">function proceedsearch(){$("body").append('<div class="popoverlay">').css("overflow","hidden"),$(".popup").toggle()}var isfetched=!1,search_path="search.xml";0==search_path.length&&(search_path="search.xml");var path="/"+search_path,searchFunc=function(e,t,a){"use strict";$.ajax({url:e,dataType:"xml",async:!0,success:function(e){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var r=$("entry",e).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get(),c=document.getElementById(t),n=document.getElementById(a);c.addEventListener("input",function(){var e=0,t='<ul class="search-result-list">',a=this.value.trim().toLowerCase().split(/[\s\-]+/);n.innerHTML="",this.value.trim().length>1&&r.forEach(function(r){var c=!1,n=r.title.trim().toLowerCase(),s=r.content.trim().replace(/<[^>]+>/g,"").toLowerCase(),o=decodeURIComponent(r.url),i=-1,l=-1,p=-1;if(""!=n&&a.forEach(function(e,t){i=n.indexOf(e),l=s.indexOf(e),(i>=0||l>=0)&&(c=!0,0==t&&(p=l))}),c){e+=1,t+="<li><a href='"+o+"' class='search-result-title'>"+n+"</a>";var h=r.content.trim().replace(/<[^>]+>/g,"");if(p>=0){var u=p-20,d=p+80;u<0&&(u=0),0==u&&(d=50),d>h.length&&(d=h.length);var f=h.substring(u,d);a.forEach(function(e){var t=new RegExp(e,"gi");f=f.replace(t,'<b class="search-keyword">'+e+"</b>")}),t+='<p class="search-result">'+f+"...</p>"}t+="</li>"}}),t+="</ul>",0==e&&(t='<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'),""==a&&(t='<div id="no-result"><i class="fa fa-search fa-5x" /></div>'),n.innerHTML=t}),proceedsearch()}})};$(".popup-trigger").click(function(e){e.stopPropagation(),0==isfetched?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(function(e){$(".popup").hide(),$(".popoverlay").remove(),$("body").css("overflow","")}),$(".popup").click(function(e){e.stopPropagation()})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script></body></html>